package com.security.wxlogin.conf;

import com.security.wxlogin.util.HttpRequestUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Classname WXAuthenticationLoginFilter
 * @Description 微信登录
 * @Date 2020/4/22 0022 15:58
 * @Created by 埔枘
 */
@Slf4j
public class WXAuthenticationLoginFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * 获取 Token 的 API
     */
    private final static String ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";
    /**
     * 获取 用户信息 的 API
     */
    private final static String USERINFO_TOKEN = "https://api.weixin.qq.com/sns/userinfo";
    /**
     * grant_type 由微信提供
     */
    private final static String GRANT_TYPE = "authorization_code";
    /**
     * appid 由微信提供
     */
    static final String appid = "xxxxxx";

    /**
     * client_secret 由微信提供
     */
    private final static String CLIENT_SECRET = "xxxxxxxxxxxx";


    protected WXAuthenticationLoginFilter(String defaultFilterProcessesUrl) {
        super(new AntPathRequestMatcher(defaultFilterProcessesUrl, HttpMethod.GET.name()));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        String code = httpServletRequest.getParameter("code");
        if(StringUtils.isBlank(code)){
            log.warn("用户拒绝授权");
            throw new CustomAccessDeniedException("用户拒绝授权");
        }else{
            //1.通过 code 获取AccessToken https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
            WXToken accessToken = getAccessToken(code);
            // 生成验证 authenticationToken
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(accessToken.getAccess_token(),accessToken.getOpenid());
            // 返回验证结果
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    /**
     * 正常返回:
     * {
     * "openid":"OPENID",
     * "nickname":"NICKNAME",
     * "sex":1,
     * "province":"PROVINCE",
     * "city":"CITY",
     * "country":"COUNTRY",
     * "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
     * "privilege":[
     * "PRIVILEGE1",
     * "PRIVILEGE2"
     * ],
     * "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
     *
     * }
     *
     * 错误返回:
     * {
     * "errcode":40003,"errmsg":"invalid openid"
     * }
     * WXUser wxUser = (WXUser) getUserInfo(accessToken.getAccessToken(), accessToken.getOpenid(),WXUser.class);
     */
    private Object getUserInfo(String accessToken,String openId,Class cls) throws IOException {
        Map<String,String> info = new HashMap<>();
        info.put("access_token",accessToken);
        info.put("openid",openId);
        return HttpRequestUtil.get(USERINFO_TOKEN, info,cls);
    }

    /**
     * 正常返回如下
     * {
     * "access_token":"ACCESS_TOKEN",
     * "expires_in":7200,
     * "refresh_token":"REFRESH_TOKEN",
     * "openid":"OPENID",
     * "scope":"SCOPE",
     * "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
     * }
     *
     * 错误返回如下:
     * {"errcode":40029,"errmsg":"invalid code"}
     *
     */
    private WXToken getAccessToken(String code) throws IOException {
        Map<String,String> map = new HashMap();
        map.put("appid",appid);
        map.put("secret",CLIENT_SECRET);
        map.put("code",code);
        map.put("grant_type",GRANT_TYPE);
        WXToken wxToken = (WXToken) HttpRequestUtil.get(ACCESS_TOKEN, map,WXToken.class);
        log.info("获取 accessCode 响应结果:{}",wxToken.toString());
        if(wxToken.getErrcode() != null){
            log.info("获取 accessCode 出错:{}",wxToken.getErrmsg());
            throw new CustomAccessDeniedException("用户授权出错");
        }
        return wxToken;
    }

    @Getter
    @Setter
    @ToString
    @AllArgsConstructor
    class WXToken {

        public WXToken(){}

        /**
         * token
         */
        private String access_token;

        /**
         * 有效期
         */
        private Double expires_in;

        /**
         * 刷新时用的 token
         */
        private String refresh_token;
        /**
         * 授权用户唯一标识
         */
        private String openid;
        /**
         * 用户授权的作用域，使用逗号（,）分隔
         */
        private String scope;
        /**
         * 当且仅当该网站应用已获得该用户的userinfo授权时，才会出现该字段。
         */
        private String unionid;

        private String errcode;
        private String errmsg;
    }
    @Getter
    @Setter
    @ToString
    @AllArgsConstructor
    class WXUser {

        public WXUser(){}

        /**
         * 普通用户的标识，对当前开发者帐号唯一
         */
        private String openid;

        /**
         * 普通用户昵称
         */
        private Double nickname;

        /**
         * 普通用户性别，1为男性，2为女性
         */
        private String sex;
        /**
         * 普通用户个人资料填写的省份
         */
        private String province;
        /**
         * 普通用户个人资料填写的城市
         */
        private String city;
        /**
         * 国家，如中国为CN
         */
        private String country;
        /**
         * 用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像），用户没有头像时该项为空
         */
        private String headimgurl;
        /**
         * 用户特权信息，json数组，如微信沃卡用户为（chinaunicom）
         */
        private String privilege;
        /**
         * 用户统一标识。针对一个微信开放平台帐号下的应用，同一用户的unionid是唯一的。
         */
        private String unionid;
    }
}
